package auth

import (
	"errors"
	"github.com/golang-jwt/jwt/v4"
	"time"
)

// JWT Life for second
type JWT struct {
	SigningKey []byte
	Life       int64
}

var (
	TokenExpired     = errors.New("Token is expired")
	TokenNotValidYet = errors.New("Token not active yet")
	TokenMalformed   = errors.New("That's not even a token")     //格式或者解析错误
	TokenInvalid     = errors.New("Couldn't handle this token:") //校验不通过
)

func NewJWTString(key string, second int64) *JWT {
	return NewJWT([]byte(key), second)
}
func NewJWT(key []byte, second int64) *JWT {
	return &JWT{
		SigningKey: key,
		Life:       second,
	}
}
func (j *JWT) NewBasicClaim(start time.Time) jwt.RegisteredClaims {
	var c = jwt.RegisteredClaims{
		ExpiresAt: jwt.NewNumericDate(start.Add(time.Second * time.Duration(j.Life))),
		NotBefore: jwt.NewNumericDate(start),
		IssuedAt:  jwt.NewNumericDate(start),
	}
	return c
}

// CreateToken 创建一个token
func (j *JWT) CreateToken(claim jwt.Claims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
	return token.SignedString(j.SigningKey)
}
func (j *JWT) CreateTokenHash(method jwt.SigningMethod, claim jwt.Claims) (string, error) {
	token := jwt.NewWithClaims(method, claim)
	return token.SignedString(j.SigningKey)
}

// ParseToken 解析token
//
//	ErrInvalidKey      = errors.New("key is invalid")
//	ErrInvalidKeyType  = errors.New("key is of invalid type")
//	ErrHashUnavailable = errors.New("the requested hash function is unavailable")
//
//	ErrTokenMalformed        = errors.New("token is malformed")
//	ErrTokenUnverifiable     = errors.New("token is unverifiable")
//	ErrTokenSignatureInvalid = errors.New("token signature is invalid")
//
//	ErrTokenInvalidAudience  = errors.New("token has invalid audience")
//	ErrTokenExpired          = errors.New("token is expired")
//	ErrTokenUsedBeforeIssued = errors.New("token used before issued")
//	ErrTokenInvalidIssuer    = errors.New("token has invalid issuer")
//	ErrTokenNotValidYet      = errors.New("token is not valid yet")
//	ErrTokenInvalidId        = errors.New("token has invalid id")
//	ErrTokenInvalidClaims    = errors.New("token has invalid claims")
//
// 无法辨认的错误默认TokenInvalid
func (j *JWT) ParseToken(claim jwt.Claims, tokenString string, options ...jwt.ParserOption) (jwt.Claims, error) {
	token, err := jwt.ParseWithClaims(tokenString, claim, func(token *jwt.Token) (i interface{}, e error) {
		return j.SigningKey, nil
	}, options...)
	if err != nil {
		if ve, ok := err.(*jwt.ValidationError); ok {
			switch true {
			//格式错误
			case errors.Is(ve, jwt.ErrInvalidKey), errors.Is(ve, jwt.ErrInvalidKeyType), errors.Is(ve, jwt.ErrHashUnavailable), errors.Is(ve, jwt.ErrTokenMalformed):
				return nil, TokenMalformed
			//校验错误
			case errors.Is(ve, jwt.ErrTokenInvalidClaims), errors.Is(ve, jwt.ErrTokenUnverifiable), errors.Is(ve, jwt.ErrTokenSignatureInvalid), errors.Is(ve, jwt.ErrTokenInvalidAudience), errors.Is(ve, jwt.ErrTokenInvalidIssuer), errors.Is(ve, jwt.ErrTokenInvalidId):
				return nil, TokenInvalid
			//过期
			case errors.Is(ve, jwt.ErrTokenExpired):
				return nil, TokenExpired
			//未生效
			case errors.Is(ve, jwt.ErrTokenUsedBeforeIssued), errors.Is(ve, jwt.ErrTokenNotValidYet):
				return nil, TokenNotValidYet
			}
		}
		return nil, TokenInvalid
	}
	if token != nil {
		if !token.Valid {
			return nil, TokenInvalid
		}
		return token.Claims, nil
	} else {
		return nil, TokenInvalid
	}
}
func (j *JWT) ParseTokenWithoutClaimsValidation(claim jwt.Claims, tokenString string) (jwt.Claims, error) {
	return j.ParseToken(claim, tokenString, jwt.WithoutClaimsValidation())
}
